<?php

//在PHP中使用协程实现多任务调度 https://www.laruence.com/2015/05/28/3038.html
//智商税 https://zhuanlan.zhihu.com/p/94145603

/**
 * 生成器提供了一种更容易的方法来实现简单的对象迭代，相比较定义类实现 Iterator 接口的方式，性能开销和复杂性大大降低。
 * 生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限，或者会占据可观的处理时间。
 * 调用生成器函数时会返回一个内部的 Generator 类的对象。 该对象实现了 Iterator 接口，基本上和仅向前的迭代器一样， 它提供的方法可以操控生成器的状态，包括发送值、返回值。
 * 生成器最主要的优点是简洁。和实现一个 Iterator 类相较而言， 同样的功能，用生成器可以编写更少的代码，可读性也更强。
 * 不过，这也付出了灵活性的代价： 生成器是一个只能向前的迭代器，一旦开始遍历就无法后退。 意思也就是说，同样的生成器无法遍历多遍：要么再次调用生成器函数，重新生成后再遍历。
 *
 *
 * 生成器语法
 * 生成器函数看起来像普通函数——不同的是普通函数返回一个值，而生成器可以 yield 生成多个想要的值。 任何包含 yield 的函数都是一个生成器函数。
 * 当一个生成器被调用的时候，它返回一个可以被遍历的对象.
 * 生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明，不同之处在于普通return会返回值并终止函数的执行，而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
 * yield $value;
 * 指定键名来生成值 yield $key => $value;
 *
 * The Generator class
Generator::current — Get the yielded value
Generator::getReturn — Get the return value of a generator
Generator::key — Get the yielded key
Generator::next — Resume execution of the generator
Generator::rewind — Rewind the iterator
Generator::send — Send a value to the generator
Generator::throw — Throw an exception into the generator
Generator::valid — Check if the iterator has been closed
Generator::__wakeup — Serialize callback
 *
 *
 * Errors
 * Fatal error: The "yield" expression can only be used inside a function
 * Fatal error: Uncaught Exception: Cannot rewind a generator that was already run
 * Fatal error: Uncaught Exception: Cannot get return value of a generator that hasn't returned
 * Fatal error: Uncaught ArgumentCountError: Generator::current() expects exactly 0 arguments, 1 given
*/

// yield 1; // Fatal error: The "yield" expression can only be used inside a function

function printer() {
    echo "I'm printer!".PHP_EOL;
        $string = yield;
        echo $string.PHP_EOL;
    echo yield  'input', PHP_EOL;
    echo yield , PHP_EOL;
    echo yield , PHP_EOL;
    yield 'end';
}

$printer = printer();
$printer->send('Hello world!');
var_dump($printer->send('Hello world2!'));
$printer->send('Hello world3!');
$printer->send('Hello world4!');
//var_dump($printer->current('Hello world5!')); // Fatal error: Uncaught ArgumentCountError: Generator::current() expects exactly 0 arguments, 1 given
var_dump($printer->current()); // string(3) "end"
$printer->send('Hello world5!');
echo '----foo----', PHP_EOL;

function foo() {
    yield 1;
    yield 'li';
    echo yield 2, PHP_EOL; // yield 2;
    yield 'say';
    yield 3;
    echo 'execute----', PHP_EOL;
    $data = yield 'I am key' => 'hello';
    var_dump($data);
    yield 4;
    yield 'I am key' => 'world';
    return 'world';
}
$foo = foo();
var_dump($foo); //object(Generator)#1 (0) {}
//var_dump($foo->getReturn()); //Fatal error: Uncaught Exception: Cannot get return value of a generator that hasn't returned
var_dump($foo->key(), $foo->current(), $foo->next(), $foo->key(), $foo->current()); // int(0) int(1) NULL int(1) string(2) "li"
// $foo->rewind(); // Fatal error: Uncaught Exception: Cannot rewind a generator that was already run
var_dump($foo->valid()); // bool(true)
$foo->next();
echo '----send----', PHP_EOL;
var_dump($foo->send('what')); // int(2)
echo '----while----', PHP_EOL;
$i = 0;
while ($foo->valid()) {
    if ($i == 2) {
        $foo->send('what' . PHP_EOL);
    } else {
        echo vsprintf('key: %s => value: %s', [$foo->key(), $foo->current()]), PHP_EOL;
        $foo->next();
    }
    $i ++;
}
var_dump($foo->valid()); // bool(false)





































